import * as crypto from "crypto";
import dayjs from 'dayjs-shanghai';
import { unix } from 'dayjs';

/**
 * 工具相关模块
 */
export module UtilsMod {
    /**
     * 获取对象的属性长度
     * @param obj 对象信息
     * @returns 
     */
    export function size(obj: { [key: string]: any }): number {
        let num = 0;
        for (let x in obj) {
            num++;
        }
        return num;
    }

    /**
     * 判断是否是字符串类型
     * @param x 
     */
    export function isString(x: any): x is string {
        return typeof x === "string";
    }

    /**
     * 判断对象是否为空
     * @param obj 
     */
    export function isEmptyObj(obj: Object): boolean {
        for (let x in obj) {
            return false;
        }
        return true;
    }

    /**
     * 随机获取数组下标
     */
    export function randIntNum(num: number) {
        return Math.floor(Math.random() * num);
    }

    export function randBetween(min: number, max: number): number {
        return min + randIntNum(max - min + 1);
    }

    /**
     * 获取随机数字
     * @param lowerLimit 最小数
     * @param higherLimit 最大数字
     * @param type 类型
     */
    export const getRandNum = (lowerLimit: number, higherLimit: number, type: 'floor' | 'ceil' | 'round' = 'round'): number => {
        if (higherLimit < lowerLimit) {
            const tmpNum: number = lowerLimit;
            lowerLimit = higherLimit;
            higherLimit = tmpNum;
        }
        const num: number = Math.random() * (higherLimit - lowerLimit);
        let randNum = 0;
        switch (type) {
            case 'floor':
                randNum = Math.floor(num);
                break;
            case 'ceil':
                randNum = Math.ceil(num);
                break;
            case 'round':
                randNum = Math.round(num);
                break;
        }
        return lowerLimit + randNum;
    }

    /**
     * 判断是否是一个合法的数字
     * @param num 数字
     * @param flag 0判断是否>=0, 1判断是否是正整数,2判断是否是数字(不包括小数),其他判断是否是一个有效数字
     * @returns true校验成功 false校验失败
     */
    export function checkNumber(num: number, flag: number = 1): boolean {
        if(typeof(num) !== "number" || num === undefined || isNaN(num)) {
            return false;
        }
        switch (flag) {
            case 0: return /^[0-9]+$/.test(String(num));
            case 1: return (/^[0-9]+$/.test(String(num)) && num > 0);
            case 2: return /^\-?[0-9]+$/.test(String(num));
            default: return /^\-?[0-9]*\.?[0-9]+$/.test(String(num));
        }
    }

    /**
     * 判断是否为有效的json
     * @param jsonStr json字符串
     * @returns 
     */
    export function isValidJson(jsonStr: string): boolean {
        try {
            JSON.parse(jsonStr);
            return true;
        } catch (e) {
            return false;
        }
    }

    /**
     * 校验是否合法的appid
     * @param appId
     */
    export function isAppId(appId: string): boolean {
        const regex = /^wx[a-zA-Z0-9\-]{10,20}$/;
        return regex.test(appId);
    }

    /**
     * 休眠
     * @param milliseconds 多少毫秒
     * @returns 
     */
    export function sleep(milliseconds: number): Promise<void> {
        return new Promise((resolve) => setTimeout(resolve, milliseconds));
    }

    /**
     * 重试方法
     * @param callback 回调函数
     * @param maxAttempts 最大尝试次数
     * @param sleepMilliseconds 休眠的时间 
     * @returns 
     */
    export async function retry<T>(callback:() => Promise<T>, maxAttempts: number = 3, sleepMilliseconds: number = 0): Promise<T|null> {
        let attempts: number = 0;
        while(attempts < maxAttempts) {
            try {
                const result = await callback();
                return result;
            }catch(e){
                attempts++;
                if (attempts === maxAttempts) {
                    break;
                }
                await sleep(sleepMilliseconds);
            }
        }
        return null;
    }

    /**时间相关工具---BEGIN */

    /**
     * 获取区服开服了几天
     * @param serverOpenUnix 开服时间戳
     * @param curUnix 当前时间戳(当前时间是需要大于等于开服时间的)
     * @returns 
     */
    export function getServerOpened(serverOpenUnix: number, curUnix: number): number {
        if(curUnix < serverOpenUnix) {
            return 0;
        }
        return getDayDiff(serverOpenUnix, curUnix);
    }

    function getDayDiff(unix1: number, unix2: number): number {
        //unix1 > unix2的时候是正数
        const date1: string = unix(unix1).format('YYYY-MM-DD');
        const date2: string = unix(unix2).format('YYYY-MM-DD');
        const diff: number = dayjs(date1).diff(date2, 'day');
        return diff + 1; //多少天
    }

    /**
     * 获取一周的所有日期
     * @param nowUnix 时间戳不传默认当前时间
     * @param format 时间格式
     * @returns
     */
    export function getWeekDates(nowUnix: number = 0, format: string = 'YYYY-MM-DD'): string[] {
        const now = (nowUnix <= 0) ? dayjs() : unix(nowUnix);
        const dayOfWeek = now.day(); // 获取当前日期是本周的第几天（0表示周日，1表示周一，以此类推）
        // 计算偏移天数，使得星期一为本周的第一天
        const offsetMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
        // 计算本周的起始日期（周一）
        const monday = now.subtract(offsetMonday, 'day');
        // 使用 Array.from 生成包含本周所有日期的数组
        const weekDates = Array.from({ length: 7 }, (_, index) =>
            monday.add(index, 'day').format(format)
        );
        return weekDates;
    }

    /**时间相关工具---END */

    /**
     * 随机数组中的一个元素
     * @param arr 
     */
    export function randArrElement<T>(arr: T[]): T {
        return arr[randIntNum(arr.length)];
    }

    export function removeFromArr<T>(arr: T[], one: T) {
        let index = arr.indexOf(one);
        if (index !== -1) {
            arr.splice(index, 1);
        }
    }

    /**
     * 获取随机字符串
     * @param len 长度
     * @returns 
     */
    export function randStr(len = 8): string {
        let charArr: string[] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.split("");
        let str = "";
        for (let i = 0; i < len; i++) {
            str += randArrElement(charArr);
        }
        return str;
    }
    
    /**
     * 加密（不可逆）
     * @param data 
     * @returns 
     */
    export function md5(data: string) {
        let cipher = crypto.createHash("md5");
        return cipher.update(data).digest("hex");
    }

    /**
     * 设置第n位为1或0
     * @param num 
     * @param index 
     * @param is_1 是否置为1
     */
    export function setBit(num: number, n: number, is_1 = true) {
        if (is_1) {
            return num | (1 << n);
        } else {
            return num & (~(1 << n));
        }
    }

    /**
     * 获取第n位
     * @param num 
     * @param n 
     */
    export function getBit(num: number, n: number) {
        return (num & (1 << n)) >> n;
    }

    /**
     * 计算两个坐标之间的距离
     * @param pos1 坐标1
     * @param pos2 坐标2
     */
    export function getLen(pos1: { "x": number, "y": number }, pos2: { "x": number, "y": number }) {
        return Math.sqrt(getLen2(pos1, pos2));
    }

    /**
     * 计算两个坐标之间的距离平方
     * @param pos1 坐标1
     * @param pos2 坐标2
     */
    export function getLen2(pos1: { "x": number, "y": number }, pos2: { "x": number, "y": number }) {
        return (pos1.x - pos2.x) * (pos1.x - pos2.x) + (pos1.y - pos2.y) * (pos1.y - pos2.y);
    }

    /**
     * 沿向量移动后的坐标
     * @param start 起始坐标
     * @param end 终点坐标
     * @param moveDis 移动的距离
     * @param length 总长度
     */
    export function getLerpPos(start: { "x": number, "y": number }, end: { "x": number, "y": number }, moveDis: number, length?: number) {
        if (!length) {
            length = getLen(start, end);
        }
        var pos = {} as { "x": number, "y": number };

        pos.x = start.x + (end.x - start.x) * (moveDis / length);
        pos.y = start.y + (end.y - start.y) * (moveDis / length);

        return pos;
    }
}